package com.zxy.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zxy.pojo.entity.SysOperLog;
import com.zxy.service.SysOperLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

// 注意：Spring Boot 3.x+ 用 jakarta.servlet，不要用 javax.servlet
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class AnnotationOperLogAspect {

    @Autowired
    private SysOperLogService sysOperLogService;

    // 确保OperLog注解路径正确（你的注解在com.zxy.util包下）
    @Pointcut("@annotation(com.zxy.util.OperLog)")
    public void operLogPointCut() {}

    @AfterReturning(pointcut = "operLogPointCut()", returning = "result")
    public void saveOperLog(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null, result);
    }

    @AfterThrowing(pointcut = "operLogPointCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    private void handleLog(JoinPoint joinPoint, Exception e, Object result) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }

        // 移除不必要的强制转换（ServletRequestAttributes.getRequest()已返回HttpServletRequest）
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperLog operLogAnnotation = method.getAnnotation(OperLog.class);

        SysOperLog operLog = new SysOperLog();

        // 1. 从注解获取配置（关键：businessTypes数组转JSON字符串）
        operLog.setTitle(operLogAnnotation.title());
        operLog.setBusinessType(operLogAnnotation.businessType());
        // 核心修改：将单个业务类型转为数组，再转JSON字符串（适配数据库varchar字段）
        Integer[] businessTypeArr = new Integer[]{operLogAnnotation.businessType()};
        operLog.setBusinessTypes(JSONUtil.toJsonStr(businessTypeArr)); // 转为JSON字符串（如["1"]）

        // 2. 自动填充基础信息
        operLog.setOperTime(LocalDateTime.now());
        operLog.setCreateTime(LocalDateTime.now());
        operLog.setUpdateTime(LocalDateTime.now());
        operLog.setOperIp(getIpAddress(request));
        operLog.setOperUrl(request.getRequestURI());
        operLog.setRequestMethod(request.getMethod());
        operLog.setMethod(method.getDeclaringClass().getName() + "." + method.getName() + "()");
        operLog.setOperatorType(1); // 1=后台用户，可根据实际调整
        operLog.setOperLocation(getLocationByIp(operLog.getOperIp()));

        // 3. 获取用户信息（按你的Token逻辑修改，这里是模拟）
        String token = request.getHeader("Authorization");
        if (StringUtils.isNotBlank(token)) {
            Map<String, Object> userInfo = parseToken(token);
            operLog.setOperId((Long) userInfo.get("userId"));
            operLog.setOperName((String) userInfo.get("userName"));
            operLog.setCreateBy((String) userInfo.get("userName"));
            operLog.setUpdateBy((String) userInfo.get("userName"));
        } else {
            operLog.setOperName("匿名用户");
            operLog.setCreateBy("系统");
            operLog.setUpdateBy("系统");
        }

        // 4. 填充请求参数和响应结果（转JSON字符串）
        if (operLogAnnotation.saveParams()) {
            operLog.setOperParam(getRequestParams(joinPoint));
        }
        if (result != null && operLogAnnotation.saveResult()) {
            operLog.setJsonResult(JSONUtil.toJsonStr(result));
        }

        // 5. 异常信息处理
        if (e != null) {
            operLog.setStatus(1); // 1=异常
            operLog.setErrorMsg(StrUtil.sub(e.getMessage(), 0, 2000)); // 限制长度，避免数据库字段溢出
        } else {
            operLog.setStatus(0); // 0=正常
        }

        // 6. 存入数据库（此时字段类型匹配，无类型转换问题）
        sysOperLogService.save(operLog);
    }

    // ---------------------- 工具方法（不变）----------------------
    /**
     * 模拟Token解析（实际项目替换为真实逻辑，如JWT解析）
     */
    private Map<String, Object> parseToken(String token) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("userId", 1L); // 模拟用户ID
        userInfo.put("userName", "admin"); // 模拟用户名
        return userInfo;
    }

    /**
     * 获取真实IP地址（处理反向代理场景）
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 处理本地IP（0:0:0:0:0:0:0:1转为127.0.0.1）
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    /**
     * 简单IP定位（实际可调用高德/百度IP接口）
     */
    private String getLocationByIp(String ip) {
        if ("127.0.0.1".equals(ip) || ip.startsWith("192.168.")) {
            return "内网IP";
        }
        return "未知位置";
    }

    /**
     * 获取请求参数（转JSON字符串，过滤敏感参数）
     */
    private String getRequestParams(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = signature.getParameterNames();
        Object[] paramValues = joinPoint.getArgs();
        Map<String, Object> params = new HashMap<>();
        if (paramNames != null && paramNames.length > 0) {
            for (int i = 0; i < paramNames.length; i++) {
                // 敏感参数过滤（如密码不存入日志）
                if ("password".equals(paramNames[i])) {
                    params.put(paramNames[i], "******");
                } else {
                    params.put(paramNames[i], paramValues[i]);
                }
            }
        }
        return JSONUtil.toJsonStr(params);
    }
}